#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This program is free software; you can redistribute it and/or modify  
# it under the terms of the GNU General Public License as published by  
# the Free Software Foundation; either version 2 of the License, or     
# (at your option) any later version.                                   
#                                                                         
# A copy of the license can be found in the license.txt file supplied   
# with this software or at: http://www.gnu.org/copyleft/gpl.html       
#
#
# sleepyindex -- An index with SleepyCat BerkeleyDB
#

import struct

from bsddb import db

from icewood import tinylog


class   SleepyIndex(object) :
    
    HDB_INUM = 4
    HDB_BNUM = 1048576 * 5
    HDB_PAGE_SIZE = 1024
    HDB_FFACTOR = 0
    HDB_CACHE_SIZE = 268435456

    BDB_INUM = 4
    BDB_PAGE_SIZE = 2048
    BDB_MIN_KEY = 36
    BDB_CACHE_SIZE = 268435456

    def __init__(self, filename, options=None) :
        self._logger = tinylog.getLogger( self.__class__.__name__ )

        self._db_filename = filename

        self._logger.info('initializing Sleepy index %s ...',self._db_filename)
        
        if options :
            if 'hdb' == options['engine'] :
                self.__initHDB( options )
            elif 'bdb' == options['engine'] :
                self.__initBDB( options )
            else :
                self.__initBDB( options )
        else :        
            self.__initBDB( options )

        self._logger.info('initialize Sleepy index %s done.',self._db_filename)
        ## __init__()

    def __initBDB(self, options) :
        if options :
            try :
                inum = options['inum']
            except :
                inum = self.BDB_INUM

            try :
                page_size = options['pagesize']
            except :
                page_size = self.BDB_PAGE_SIZE

            try :
                min_key = options['minkey']
            except :
                min_key = self.BDB_MIN_KEY

            try :
                cache_size = options['cachesize']
            except :
                cache_size = self.BDB_CACHE_SIZE
        else :
            inum, page_size, min_key = self.BDB_INUM, self.BDB_PAGE_SIZE, self.BDB_MIN_KEY
            cache_size = self.BDB_CACHE_SIZE

        self._dbs = []
        for i in xrange(inum) :
            dbh = db.DB()
            if page_size > 128 :
                dbh.set_pagesize( page_size )
            if cache_size > 0 :
                dbh.set_cachesize(0, cache_size, 1)
            if min_key > 0 :
                dbh.set_bt_minkey( min_key )
            filename = '%s-i%02X' % (self._db_filename, i)
            dbh.open(filename, dbtype=db.DB_BTREE, flags=db.DB_THREAD|db.DB_CREATE, mode=0664)

            self._dbs.append( dbh )
        self._dbs_count = inum
        ## __initBDB()

    def __initHDB(self, options) :
        if options :
            try :
                inum = options['inum']
            except :
                inum = self.HDB_INUM

            try :
                bnum = options['bnum']
            except :
                bnum = self.HDB_BNUM

            try :
                page_size = options['pagesize']
            except :
                page_size = self.HDB_PAGE_SIZE

            try :
                ffactor = options['ffactor']
            except :
                ffactor = self.HDB_FFACTOR

            try :
                cache_size = options['cachesize']
            except :
                cache_size = self.HDB_CACHE_SIZE
        else :
            inum, bnum,page_size,ffactor = self.HDB_INUM, self.HDB_BNUM, self.HDB_PAGE_SIZE, self.HDB_FFACTOR
            cache_size = self.HDB_CACHE_SIZE

        db_flags = 0

        self._dbs = []
        for i in xrange(inum) :
            dbh = db.DB()
            if page_size > 128 :
                dbh.set_pagesize( page_size )
            if cache_size > 0 :
                dbh.set_cachesize(0, cache_size, 1)
            if ffactor > 0 :
                dbh.set_h_ffactor( ffactor )
            if bnum > 0 :
                dbh.set_h_nelem( bnum )
            filename = '%s-i%02X' % (self._db_filename, i)
            dbh.open(filename, dbtype=db.DB_HASH, flags=db.DB_THREAD|db.DB_CREATE, mode=0664)

            self._dbs.append( dbh )
        self._dbs_count = inum
        ## __initHDB()

    def __del__(self) :
        self.close()
        ## __del__()

    def close(self) :
        if self._dbs :
            for db in self._dbs :
                db.sync()
                db.close()
                db = None

            self._dbs = []
        ## close()

    def flush(self) :
        if self._dbs :
            for db in self._dbs :
                db.sync()
        ## flush()

    def put(self, key, value) :
        db = self.__getDB( key )
        key_s = self.__getStoreKey( key )
        db.put( key_s, value )
        ## put()

    def get(self, key) :
        db = self.__getDB( key )
        key_s = self.__getStoreKey( key )
        return db.get( key_s )
        ## get()

    def delete(self, key) :
        db = self.__getDB( key )
        key_s = self.__getStoreKey( key )
        db.delete( key_s )

        return True
        ## delete()


    def vanish(self) :
        for db in self._dbs :
            db.truncate()
        ## vanish()

    ###################################################################

    def __getHashCode(self, key) :
        k_idx = struct.unpack('>H', key[4:6])[0]

        return k_idx % self._dbs_count
        ## __getDB()

    def __getDB(self, key) :
        idx = self.__getHashCode( key )

        return self._dbs[ idx ]
        ## __getDB()
 
    def __getStoreKey(self, key) :
        return  key[:8]
        ## __getStoreKey()

    ## class SleepyIndex


